package genetic.core;

import static genetic.core.GeneticAlgorithmConstants.SELECTION_PRESSURE;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * Simple strategy for picking up best {@link Individual}s.
 *  
 * @author vasek
 *
 */
public class TournamentSelection implements Selection {

	private double mutationProbability;
	private double crossoverProbability;
	
	/**
	 * Constructor.
	 * 
	 * @param mutationProbability how much probable is that individual will mutate.
	 * @param crossoverProbability how much probable is that crossover will be performed.
	 */
	public TournamentSelection(double mutationProbability,
			double crossoverProbability) {
		super();
		this.mutationProbability = mutationProbability;
		this.crossoverProbability = crossoverProbability;
	}

	@Override
	public Individual reproduce(List<Individual> population) {
		final double mutation = new Random().nextDouble();
		final double crossover = new Random().nextDouble();
		final Random random1 = new Random();
		final Random random2 = new Random();
		final Individual[] tournament1 = new Individual[SELECTION_PRESSURE];
		final Individual[] tournament2 = new Individual[SELECTION_PRESSURE];
		
		for (int i = 0; i < tournament1.length; i++) {
			final int index = random1.nextInt(population.size());
			tournament1[i] = population.get(index);			
		}
		
		for (int i = 0; i < tournament2.length; i++) {
			final int index = random2.nextInt(population.size());
			tournament2[i] = population.get(index);			
		}
		
		final Individual parent1 = evaluateTournamenent(tournament1);
		final Individual parent2 = evaluateTournamenent(tournament2);
		Individual offspring = parent1.clone();
		
		if (crossover <= crossoverProbability) {
			offspring = parent1.crossover(parent2);
		}
		
		if (mutation <= mutationProbability) {
			offspring = offspring.mutate();
		}
		
		return offspring;		
	}

	/**
	 * Pick the best {@link Individual} from the tournament.
	 * 
	 * @param tournament tournament to be evaluated.
	 * @return best {@link Individual} from the the tournament.
	 */
	private Individual evaluateTournamenent(Individual[] tournament) {
		Arrays.sort(tournament);
		return tournament[tournament.length - 1];
	}
}
